package com.szwistar.common.meta;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.szwistar.common.datastruct.Catagory;
import com.szwistar.common.datastruct.Reflector;
import com.szwistar.common.datastruct.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * API 接口注册表
 * 登记哪个命令字或API名字，与哪个类的哪个函数关联，并对登记为 action 的API登记相关参数信息
 * @author 张勇 2018-3-22
 */
public class ObjectRegistry {
	static final Logger LOG = LoggerFactory.getLogger(ObjectRegistry.class);

	public static final class FieldInfo {
		Class<?> type;	// 类型
		String name;	// 变量名
		String title;
		String unit;
		String uiType;
		String rangeJson;
		String rangeJs;
		String description;
		String group;
		String enable;
		String required;
		String writable;

		public FieldInfo() {}

		public FieldInfo(Class<?> type, String name, DefField anno) {
			this.type = type;
			this.name = name;
			title = anno.title();
			unit = anno.unit();
			uiType = anno.uiType();
			rangeJson = anno.rangeJson();
			rangeJs = anno.rangeJs();
			description = anno.description();
			group = anno.group();
			enable = anno.enable();
			required = anno.required();
			writable = anno.writable();
		}

		// 从参数类字段注解信息中生成
		public FieldInfo(Field field, DefField anno) {
			this(field.getType(), field.getName(), anno);
		}

		public Class<?> getType() { return type; }
		public String getName() { return name; }
		public String getTitle() { return title; }
		public String getUiType() { return uiType; }
		public String getRangeJson() { return rangeJson; }
		public String getRangeJs() { return rangeJs; }
		public String getUnit() { return unit; }
		public String getDescription() { return description; }
		public String getGroup() { return group; }
		public String getEnable() { return enable; }
		public String getRequired() { return required; }
		public String getWritable() { return writable; }
	}

	public static final class ApiInfo {
		Class<?> clazz;	// 实现类
		int cmd;
		String name;	// 函数名称
		String alias;
		boolean vci;
		String title;
		String description;
		Class<?> paramType;

		// API 中所有参数信息列表
		List<FieldInfo> params;

		public Class<?> getClazz() { return clazz; }
		public String getName() { return name; }
		public int getCmd() { return cmd; }
		public String getAlias() { return alias; }
		public boolean isVci() { return vci; }
		public String getTitle() { return title; }
		public String getDescription() { return description; }
		public List<FieldInfo> getParams() { return params; }
		public Class<?> getParamType() { return paramType; }

		// 从注解信息中生成
		public ApiInfo(Class<?> clazz, Method method, DefApi anno) {
			this.clazz = clazz;
			cmd = anno.cmd();
			name = method.getName();
			vci = anno.vci();
			title = anno.title();
			description = anno.description();

			if(vci) {
				LOG.debug("  \tRegister VCI: {}", method);
			} else {
				LOG.debug("  \tRegister API: {}", method);
			}

			if(Utils.isEmpty(anno.alias())) {
				// 如果别名为空，则默认为函数名
				alias = name;
			} else {
				alias = anno.alias();
			}

			if(anno.paramType() != Object.class) {
				paramType = anno.paramType();

				// FIXME 在安卓上取得的 API 参数列表顺序可能与实际的不相同！从而导致在调用 API 时找不到正确的 API ！
				//Field[] pfields = anno.paramType().getDeclaredFields();
				//Type[] tparams = method.getTypeParameters();
				//Class[] cparams = method.getParameterTypes();
				//Type[] tparams2 = method.getGenericParameterTypes();
				//Annotation[][] pannos = method.getParameterAnnotations();

		        // 检查各个字段的注解
				List<Field> fields = Reflector.getFields(paramType);
				for(Field field : fields) {
		            // 提取 CoFieldDef 注解
				    DefField fieldAnno = field.getAnnotation(DefField.class);
				    if(fieldAnno == null) { continue; }
	                putParam(new FieldInfo(field, fieldAnno));
			        //LOG.debug("  \t\tRegister param: {}", fieldAnno);
				}
			}
		}

		public FieldInfo putParam(FieldInfo paramInfo) {
			if(params == null) { params = new ArrayList<FieldInfo>(); }
			params.add(paramInfo);
			return paramInfo;
		}
	}

	/**
	 * 核心对象 Meta 信息
	 */
	@JsonInclude(value = JsonInclude.Include.NON_NULL)
	public static final class ObjectMetaInfo {
		// 实现类
		Class<?> clazz;
		// 分类目录
		Catagory catagory;
		// 设备类型ID
		int typeId;
		// 显示名称
		String title;
		// 客户化描述
		String description;

		// 所有有注解的字段列表
		List<FieldInfo> fields;
		// 核心对象中所有 API 列表(包括 Action-API)
		List<ApiInfo> apis;
		// 核心对象中所有 Action-API 列表(仅包括 Action-API，不包括普通 API)
		List<ApiInfo> vcis;

		public Class<?> getClazz() { return clazz; }
		@JsonIgnore
		public Catagory getCatagory() { return catagory; }
		public int getTypeId() { return typeId; }
		public String getTitle() { return title; }
		public String getDescription() { return description; }
		public List<FieldInfo> getFields() { return fields; }
		public List<ApiInfo> getApis() { return apis; }
		public List<ApiInfo> getVcis() { return vcis; }
		public boolean hasVci() { return Utils.isNotEmpty(vcis); }

		// 从注解信息中生成
		public ObjectMetaInfo(Class<?> clazz, DefObject anno) {
			this.clazz 			= clazz;
			typeId 				= anno.typeId();
			catagory			= Catagory.getOrCreate(anno.catagory());
			title 				= anno.title();
			description 		= anno.description();
		}

		// 添加一个 字段
		public FieldInfo putField(FieldInfo fieldInfo) {
			if(fields == null) { fields = new ArrayList<FieldInfo>(); }
			fields.add(fieldInfo);
			return fieldInfo;
		}

		// 添加一个 API
		public ApiInfo putApi(ApiInfo apiInfo) {
			// 添加到 API 列表
			if(apis == null) { apis = new ArrayList<ApiInfo>(); }
			apis.add(apiInfo);
			// 如果这个 API 是 VCI 则添加到 VCI 列表中
			if(apiInfo.isVci()) {
				if(vcis == null) { vcis = new ArrayList<ApiInfo>(); }
				vcis.add(apiInfo);
			}
			return apiInfo;
		}
	}

	// 保存所有已经登记过的核心对象信息: 类型ID <-> 类名
	static HashMap<Integer, Class<?>> objTypes = new HashMap<Integer, Class<?>>();
	// 保存所有已经登记过的核心对象信息: 类名 <-> 类的MetaInfo
	static HashMap<Class<?>, ObjectMetaInfo> objMetas = new HashMap<Class<?>, ObjectMetaInfo>();

	/**
	 * 扫描并登记一个类的所有标注，登记核心对象、API接口、API参数信息
	 * @param clazz 要扫描的 类名.class
	 */
	public static ObjectMetaInfo register(Class<?> clazz) {
		// 如果已经登记过，则直接返回
		ObjectMetaInfo meta = objMetas.get(clazz);
		if(meta != null) { return meta; }

		//
        // 提取 DefObject 注解
        //
		DefObject classAnno = clazz.getAnnotation(DefObject.class);
        // 没有注解，则不能登记
        if(classAnno == null) {
        	LOG.warn("对象类型未提供可供登记的注解信息！{}", clazz);
        	return null;
        }

        // 生成 MetaInfo
        meta = new ObjectMetaInfo(clazz, classAnno);

        // 检查类型ID是否已经登记过
		Class<?> clazzOld = objTypes.get(meta.getTypeId());
		if((clazzOld != null) && (clazzOld != clazz)) {
			LOG.warn("核心对象类型ID({})冲突: {} <--> {}", meta.getTypeId(), clazz.getSimpleName(), clazzOld.getSimpleName());
			//return null;
		}

		// 登记类型
		objTypes.put(meta.getTypeId(), clazz);
		// 登记 MetaInfo
        objMetas.put(clazz, meta);
        LOG.debug("Register 对象类 {}", clazz);

		//
		// 扫描 DefField 字段
		//
		// 检查各个字段的注解
		List<Field> rawFields = Reflector.getFields(clazz);
		for(Field field : rawFields) {
			// 提取 DefField 注解
			DefField fieldAnno = field.getAnnotation(DefField.class);
			// 如果没有注解，则不登记
			if(fieldAnno == null) { continue; }
			// 放入字段登记表
			meta.putField(new FieldInfo(field, fieldAnno));
			//LOG.debug("  \t\tRegister field: {}", fieldAnno);
		}

        //
        // 扫描 API
        //
		for(Method method : Reflector.getMethods(clazz)) {
            // 提取 DefApi 注解
            DefApi methodAnno = method.getAnnotation(DefApi.class);
            if(methodAnno != null) {
				ApiInfo apiInfo = new ApiInfo(clazz, method, methodAnno);

				/* ！！！重要：由于只有 Android API26(安卓8.0，对应Java1.8) 以上版本，才支持 method.getParameters() 获取方法的参数
				 * 所以不能使用参数注解的方法！！！
				// 如果此 API 不做为 VCI 使用，则不需要解析其参数的注解
				if(apiInfo.isVci()) {
					for(Parameter param : method.getParameters()) {
						// 提取 CoApiParamDef 注解
						CoVciParamDef paramAnno = param.getAnnotation(CoVciParamDef.class);
						// 登记为 VCI 的 API 函数的所有参数都必须有注解！否则整个类的API都不予以登记！
						if(paramAnno == null) { return null; }

						apiInfo.putParam(new ApiParamInfo(param, paramAnno));
						LOG.debug("    Register param: {}", methodAnno.toString());
					}
				} */

				// 登记核心对象类的 API 接口信息
				meta.putApi(apiInfo);
			}

			// 提取 DefField for getter 注解
			DefField getterAnno = method.getAnnotation(DefField.class);
			if(getterAnno != null && method.getName().startsWith("get") && method.getName().length() > 3) {
				// 把 getter 方法名转为属性名
				String name = method.getName().substring(3,4).toLowerCase() + method.getName().substring(4);
				// 放入字段登记表
				FieldInfo info = new FieldInfo(method.getReturnType(), name, getterAnno);
				meta.putField(info);
			}
		}

		return meta;
	}

	public static ObjectMetaInfo get(Class<?> clazz) {
		return register(clazz);
	}

	/**
	 * 查找类型 ID 对应的 MetaInfo
	 */
	public static ObjectMetaInfo get(Integer typeId) {
		if(typeId == null) { return null; }
		return objMetas.get(objTypes.get(typeId));
	}

	/**
	 * 根据对象类型ID查找对应的实现类
	 */
	public static Class<?> getClass(Integer typeId) {
		if(typeId == null) { return null; }
		return objTypes.get(typeId);
	}

	/**
	 * 根据对象类型名称查找对应的实现类
	 */
	public static Class<?> getClass(String typeName) {
		for(ObjectMetaInfo mt: objMetas.values()) {
			if(mt.getTitle().equals(typeName)) {
				return mt.clazz;
			}
		}
		return null;
	}

	/**
	 * 获取所有已经注册的核心对象类
	 */
	public static HashMap<Class<?>, ObjectMetaInfo> getAll() {
		return objMetas;
	}
}
